﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Windows.Media.Imaging;
using System.Xml.Linq;
using FarseerGames.FarseerPhysics.Mathematics;
using FreeGoo.GameEngine;
using FreeGoo.GameEngine.Interfaces;
using FreeGoo.GameEngine.Renderables;
using FreeGoo.LevelClasses.BehaviorParts;
using FreeGoo.LevelClasses.LevelParts;
using FreeGoo.LevelClasses.LevelParts.BodyHookOwners;
using FreeGoo.LevelClasses.LevelParts.Particles;
using FreeGoo.LevelClasses.LevelParts.Particles.Keys;
using FreeGoo.LevelClasses.TaskParts;

namespace FreeGoo.LevelClasses
{
    public class Level : ILevel
    {
        public readonly Vector2 DefaultShowSize = new Vector2(700, 700);
        private string _bitmapName;

        public Level(Campaign campaign, string fileName)
        {
            FileName = fileName;
            FileTime = File.GetLastWriteTime(FileName);
            Campaign = campaign;
            Document = XDocument.Load(fileName);
            PopupateParts();

            if (Root.Attribute("ImageFileName") != null)
            {
                BitmapName = LevelRelativeFileName(Root.Attribute("ImageFileName").Value);
            }
        }

        public static Dictionary<string, LevelPart> LevelParts { get; private set; }
        public static Dictionary<string, TaskPart> TaskParts { get; private set; }
        public static Dictionary<string, BehaviorPart> BehaviorParts { get; private set; }
        public Campaign Campaign { get; private set; }
        public Engine Engine { get { return Campaign.CampaignHandler.Engine; } }
        public XDocument Document { get; private set; }
        public XElement Root { get { return Document.Root; } }
        public string Title { get { return Root.Attribute("Title").Value; } }
        public string Description { get { return Root.Element("Description").Value; } }
        public string FileName { get; set; }
        public IEnumerable<XElement> XmlLevelParts { get { return Root.Element("LevelParts").Elements(); } }
        public Vector2 Size { get { return Root.Attribute("Size").ToVector2(); } }
        public Vector2 ShowSize { get { return Root.Attribute("ShowSize").ToVector2(DefaultShowSize); } }
        public Vector2 PositionOffset { get; internal set; }
        public DateTime FileTime { get; protected set; }
        public BitmapImage BitmapImage { get; protected set; }
        public string BitmapName
        {
            get
            {
                return _bitmapName;
            }

            set
            {
                _bitmapName = value;
                if (!String.IsNullOrEmpty(_bitmapName))
                {
                    BitmapImage = new BitmapImage();
#if !SILVERLIGHT
                    BitmapImage.BeginInit();
#endif

                    BitmapImage.UriSource = new Uri(new Uri(FileName), _bitmapName);

#if !SILVERLIGHT
                    BitmapImage.EndInit();
#endif
                }
                else
                {
                    BitmapImage = null;
                }
            }
        }

        public bool LevelFileChangedOnDisk
        {
            get
            {
                return FileTime != File.GetLastWriteTime(FileName);
            }
        }

        public void CopyToEngine(Engine engine)
        {
            PositionOffset = new Vector2(0, 0);
            AddLevelParts(XmlLevelParts);
            AddTask();
            // AddScripts();
        }

        public string LevelRelativeFileName(string fileName)
        {
            Uri baseUri = new Uri(FileName);
            return new Uri(baseUri, fileName).AbsolutePath;
        }

        public LevelPart GetLevelPart(XElement xmlLevelPart)
        {
            string levelPartName = xmlLevelPart.Name.ToString();

            if (!LevelParts.ContainsKey(levelPartName))
            {
                throw new FreeGooException("Unknown level part: " + levelPartName);
            }

            return LevelParts[levelPartName];
        }

        public TaskPart GetTaskPart(XElement xmlTaskPart)
        {
            string taskPartName = xmlTaskPart.Name.ToString();

            if (!TaskParts.ContainsKey(taskPartName))
            {
                throw new FreeGooException("Unknown task part: " + taskPartName);
            }

            return TaskParts[taskPartName];
        }

        public BehaviorPart GetBehaviorPart(XElement xmlBehaviorPart)
        {
            string behaviorPartName = xmlBehaviorPart.Name.ToString();

            if (!BehaviorParts.ContainsKey(behaviorPartName))
            {
                throw new FreeGooException("Unknown behavior part: " + behaviorPartName);
            }

            return BehaviorParts[behaviorPartName];
        }

        public void AddBehaviorToRenderable(Renderable renderable, LevelPart levelPart, XElement behaviorElement)
        {
            BehaviorPart behaviorPart = GetBehaviorPart(behaviorElement);
            behaviorPart.AddBehaviorToRenderable(this, renderable, levelPart, behaviorElement);
        }

        internal void AddLevelParts(IEnumerable<XElement> xmlLevelParts)
        {
            foreach (XElement xmlLevelPart in xmlLevelParts)
            {
                AddXmlLevelPart(xmlLevelPart);
            }
        }

        internal void AddXmlLevelPart(XElement xmlLevelPart)
        {
            LevelPart levelPart = GetLevelPart(xmlLevelPart);
            levelPart.AddToEngineFromXml(this, xmlLevelPart);
        }

        /*private void AddScripts()
        {
            if (!Root.HasElement("Scripts"))
            {
                return;
            }

            IEnumerable<XElement> xmlScripts = Root.Element("Scripts").Elements("Script");

            foreach (XElement xmlScript in xmlScripts)
            {
                AddScript(xmlScript);
            }
        }

        private void AddScript(XElement xmlScript)
        {
            string fileName = LevelRelativeFileName(xmlScript.Attribute("FileName").Value);
            string scriptCode = File.ReadAllText(fileName);
            string executeMethod = xmlScript.Attribute("ExecuteMethod").Value;
            AsmHelper helper = new AsmHelper(CSScript.LoadCode(scriptCode, null, true));
            helper.Invoke(executeMethod, Engine);
        }*/

        private void AddTask()
        {
            if (Root.HasElement("LevelTask"))
            {
                XElement xmlTaskPart = Root.Element("LevelTask").FirstNode as XElement;
                TaskPart taskPart = GetTaskPart(xmlTaskPart);
                Engine.Task = taskPart.CreateTask(this, xmlTaskPart);
            }
        }

        private void PopupateParts()
        {
            if (LevelParts == null)
            {
                LevelParts = new Dictionary<string, LevelPart>();
                AddLevelPart(new CircleLevelPart());
                AddLevelPart(new RectangleLevelPart());
                AddLevelPart(new FactoryLevelPart());
                AddLevelPart(new SimpleLevelPart());
                AddLevelPart(new MultiLinkLevelPart());
                AddLevelPart(new FixedHookLevelPart());
                AddLevelPart(new BodyHookLevelPart());
                AddLevelPart(new ChainLevelPart());
                AddLevelPart(new RopeLevelPart());
                AddLevelPart(new MotorLevelPart());
                AddLevelPart(new KeyedMotorLevelPart());
                AddLevelPart(new KeyedMotorKeyLevelPart());
                AddLevelPart(new BalloonLevelPart());
                AddLevelPart(new StrongLevelPart());
                AddLevelPart(new LinkLevelPart());
                AddLevelPart(new OffsetFrameLevelPart());
                AddLevelPart(new GoalLevelPart());
            }

            if (TaskParts == null)
            {
                TaskParts = new Dictionary<string, TaskPart>();
                AddTaskPart(new And());
                AddTaskPart(new NeverCompleted());
            }

            if (BehaviorParts == null)
            {
                BehaviorParts = new Dictionary<string, BehaviorPart>();
                AddBehaviorPart(new RotatorBehaviorPart());
                AddBehaviorPart(new ResistsCollissionBehaviorPart());
            }
        }

        private void AddLevelPart(LevelPart levelPart)
        {
            LevelParts.Add(levelPart.Name, levelPart);
        }

        private void AddTaskPart(TaskPart taskPart)
        {
            TaskParts.Add(taskPart.Name, taskPart);
        }

        private void AddBehaviorPart(BehaviorPart behaviorPart)
        {
            BehaviorParts.Add(behaviorPart.Name, behaviorPart);
        }
    }
}